package com.Reactor3参考文档;

import java.time.Duration;

import org.junit.Ignore;
import org.junit.Test;

import reactor.core.Exceptions;
import reactor.core.publisher.Flux;

public class Retry {

	@Test
	@Ignore
	// 还有一个用于错误处理的操作符你可能会用到，就是 retry，见文知意，用它可以对出现错误的序列进行重试。
	// 问题是它对于上游 Flux 是基于重订阅（re-subscribing）的方式。这实际上已经一个不同的序列了， 发出错误信号的序列仍然是终止了的。为了验证这一点，我们可以在继续用上边的例子，增加一个 retry(1) 代替 onErrorReturn 来重试一次。
	// 输出如下：
	// 259,tick 0
	// 249,tick 1
	// 251,tick 2
	// 506,tick 0
	// 248,tick 1
	// 253,tick 2
	// java.lang.RuntimeException: boom
 	// 一个新的 interval 从 tick 0 开始。多出来的 250ms 间隔来自于第 4 次 tick， 就是导致出现异常并执行 retry 的那次（译者加：我在机器上测试的时候 elapsed “显示”的时间间隔没有加倍，但是确实有第 4 次的间隔）。
	// 可见， retry(1) 不过是再一次从新订阅了原始的 interval，从 tick 0 开始。第二次， 由于异常再次出现，便将异常传递到下游了
	public void test() throws InterruptedException{
		Flux.interval(Duration.ofMillis(250))
	    .map(input -> {
	        if (input < 3) return "tick " + input;
	        throw new RuntimeException("boom");
	    })
	    // elapsed 会关联从当前值与上个值发出的时间间隔（译者加：如下边输出的内容中的 259/249/251…​）。
	    .elapsed()
	    .retry(1)
	    .subscribe(System.out::println, System.err::println);

		Thread.sleep(2100);
	}

	@Test
	@Ignore
	// 还有一个“高配版”的 retry （retryWhen），它使用一个伴随（"companion"） Flux 来判断对某次错误是否要重试。这个伴随 Flux 是由操作符创建的，但是由开发者包装它， 从而实现对重试操作的配置。
	// 这个伴随 Flux 是一个 Flux<Throwable>，它作为 retryWhen 的唯一参数被传递给一个 Function，你可以定义这个 Function 并让它返回一个新的 Publisher<?>。重试的循环 会这样运行：
	// 每次出现错误，错误信号会发送给伴随 Flux，后者已经被你用 Function 包装。
	// 如果伴随 Flux 发出元素，就会触发重试。
	// 如果伴随 Flux 完成（complete），重试循环也会停止，并且原始序列也会 完成（complete）。
	// 如果伴随 Flux 产生一个错误，重试循环停止，原始序列也停止 或 完成，并且这个错误会导致 原始序列失败并终止。
	// 了解前两个场景的区别是很重要的。如果让伴随 Flux 完成（complete）等于吞掉了错误。如下代码用 retryWhen 模仿了 retry(3) 的效果：
	public void test2(){
		Flux<String> flux = Flux
		    .<String>error(new IllegalArgumentException())
		    .doOnError(System.out::println)
		    .retryWhen(companion -> companion.take(3));
		flux.subscribe();
	}

	@Test
	public void test3(){
		Flux<String> flux =
		Flux.<String>error(new IllegalArgumentException())
		    // 技巧一：使用 zip 和一个“重试个数 + 1”的 range
			// zip 方法让你可以在对重试次数计数的同时，仍掌握着原始的错误（error）
			.retryWhen(companion -> companion.zipWith(Flux.range(1, 4),
				(error, index) -> {
					// 允许三次重试，小于 4 的时候发出一个值
		            if (index < 4) {
		            	System.out.println("retry once!");
		            	return index;
		            }
		            // 为了使序列以错误结束。我们将原始异常在三次重试之后抛出
		            // 类似的代码也可以被用于实现 exponential backoff and retry 模式 （译者加：重试指定的次数, 且每一次重试之间停顿的时间逐渐增加）
		            else throw Exceptions.propagate(error);
				})
		    );
		flux.subscribe();
	}

}
